package org.com.cis.cf;

import java.io.File;
import java.io.FileNotFoundException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Scanner;

import org.com.cis.cf.measure.DCGScorer;
import org.com.cis.cf.measure.IDCGScorer;
import org.com.cis.cf.util.TestPoint;


public class PopMF extends MF{
	
	/**
	 * init model
	 * @param userNum
	 * @param itemNum
	 * @param factorNum
	 * @param learnRate
	 * @param regular
	 * @param maxIter
	 */
	public void initMolde( int userNum, int itemNum, int factorNum, double learnRate, double regular, int maxIter)
	{
		this.userNumber = userNum;
		this.itemNumber = itemNum;
		this.factorNumber = factorNum;
		
		this.learnRate = learnRate;
		this.regularization = regular;
		
		this.maxIteration = maxIter;
	
		
		this.trainDist = new int[this.userNumber + 1][5];
		this.testDist = new int[this.userNumber + 1][5];
		
		this.trainIDCG = new double[this.userNumber + 1];
		this.testIDCG = new double[this.userNumber + 1];
		
		itemRatingSum = new int[itemNumber + 1];
		itemRatingNum = new int[itemNumber + 1];
		
		testSet = new ArrayList<ArrayList<Integer>>();
		for ( int u = 0; u <= userNumber; ++u) {
			testSet.add(new ArrayList<Integer>());
		}
		
		this.trainDist = new int[this.userNumber + 1][5];
		this.testDist = new int[this.userNumber + 1][5];
		
	}
	
	/**
	 * read train and test data
	 * @param trainPath
	 * @param testPath
	 * @throws FileNotFoundException
	 */
	public void readData( String trainPath, String testPath) throws FileNotFoundException
	{
		Scanner trainScan = new Scanner(new File(trainPath));
		int user, item, rate, idx = 0;
		while(trainScan.hasNext()) {
			user = trainScan.nextInt();
			item = trainScan.nextInt();
			rate = trainScan.nextInt();
			trainDist[user][5-rate]++;
			itemRatingSum[item] += rate;
			itemRatingNum[item] += 1;
			idx++;
			//trainSet.get(user).add(item * 10 + rate);
		}
		trainScan.close();
		System.out.print("read train data: " + idx + "\t");
		
		Scanner testScan = new Scanner(new File(testPath));
		idx = 0;
		while(testScan.hasNext()) {
			user = testScan.nextInt();
			item = testScan.nextInt();
			rate = testScan.nextInt();
			testDist[user][5-rate]++;
			idx++;
			testSet.get(user).add(item * 10 + rate);
		}
		testScan.close();
		System.out.print("read test data: " + idx + "\n");
 	}
	
	double predict( int user, int item)
	{
		double res = 0.0;
		if(itemRatingNum[item] != 0)
			res = (double) itemRatingSum[item] / (double) itemRatingNum[item];
		return res;
	}
	
	double calcNdcg( ArrayList<ArrayList<Integer>> dataSet, int k, Boolean k_flag)
	{
		double ndcg = 0.0;
		int user, item, rate;
		int validUser = 0;
		
		DCGScorer scorer = new DCGScorer();
		scorer.setK(k);
		IDCGScorer iscorer = new IDCGScorer();
		iscorer.setK(k);
		
		for ( user = 1; user <= userNumber; ++user) {
			int testSize = dataSet.get(user).size();
			if (testSize == 0)
				continue;
			
			validUser++;
			
			ArrayList<TestPoint> testList = new ArrayList<TestPoint>();
			for( int i = 0; i != testSize; ++i) {
				item = dataSet.get(user).get(i) / 10;
				rate = dataSet.get(user).get(i) % 10;
				testList.add(new TestPoint(rate, predict(user, item)));
			}
			
			double dcg = scorer.score(testList, k_flag);
			double idcg = iscorer.score(testDist[user][0], testDist[user][1], testDist[user][2], testDist[user][3], testDist[user][4], k_flag);
			//double idcg = iscorer.score(testList, k_flag);
			ndcg += dcg / idcg;
		}
		
		ndcg /= validUser;
		
		return ndcg;
	}
	
	public void runModel() {

			double testNdcg1 = calcNdcg(testSet, 1, true);
			double testNdcg2 = calcNdcg(testSet, 2, true);
			double testNdcg3 = calcNdcg(testSet, 3, true);
			double testNdcg4 = calcNdcg(testSet, 4, true);
			double testNdcg5 = calcNdcg(testSet, 5, true);
			double testNdcg10 = calcNdcg(testSet, 10, true);
			NumberFormat nFormat=NumberFormat.getNumberInstance(); 
		    nFormat.setMaximumFractionDigits(4);
		    
		    System.out.print("\t" + nFormat.format(testNdcg1) + "  " + 
		    		nFormat.format(testNdcg2) + "  " + 
		    		nFormat.format(testNdcg3) + "  " + 
		    		nFormat.format(testNdcg4) + "  " + 
		    		nFormat.format(testNdcg5) + "  " + 
		    		nFormat.format(testNdcg10));
		    
		    System.out.println();


	}
	
	int itemRatingSum[];
	int itemRatingNum[];
}
